// 在 JavaScript 中，this关键字指的是当前正在执行代码的对象。以下看不同情况下的 this。

/**
 * @description 全局上下文
 */
// 默认情况下，this 指的是全局对象。
console.log(this === window); // true

/**
 * @description 函数上下文
 */
// 在函数中，在严格模式下，this 是 undefined。
function f1() {
  'use strict';
  return this;
}
console.log(f1()); // undefined

// 在函数中，当非严格模式时，this 指的是全局对象。
function f2() {
  return this;
}
console.log(f2() === window); // true

/**
 * @description 对象上下文
 */
// 在对象方法中，this 指的是调用该方法的对象。
const obj1 = {
  f: function() {
    return this;
  }
};
const myObj1 = Object.create(obj1);
myObj1.foo = 1;
console.log(myObj1.f()); // { foo: 1 }

// 在构造函数调用中，this 绑定到正在构造的新对象。
class C {
  constructor() {
    this.x = 10;
  }
}
const obj2 = new C();
console.log(obj2.x); // 10

/**
 * @description 事件处理程序上下文
 */
// 在事件处理程序中，this 绑定到放置侦听器的元素。
const el = document.getElementById('my-el');
el.addEventListener('click', function() {
  console.log(this === el); // true
});

/**
 * @description 箭头函数上下文
 */
// 在箭头函数中，this 保留封闭词法上下文的 this.
const f3 = () => this;
console.log(f3() === window); // true

const obj3 = {
  foo: function() {
    const baz = () => this;
    return baz();
  },
  bar: () => this
};
console.log(obj3.foo()); // { foo: [Function: foo], bar: [Function: bar] }
console.log(obj3.bar() === window); // true

/**
 * @description 显式改变 this
 */
// Function.prototype.bind() 从现有函数返回一个新函数，其中 this 永久绑定到 的第一个参数 bind()。
function f4() {
  return this.foo;
}

var x = f4.bind({foo: 'hello'});
console.log(x()); // 'hello'

// Function.prototype.call() 或 Function.prototype.apply()将 被调用的函数绑定 this 到这些函数中任何一个的第一个参数，仅用于此调用。
function f5() {
  return this.foo;
}

console.log(f5.call({foo: 'hi'})); // 'hi'